#ifndef __TESTS_LIB_H__
#define __TESTS_LIB_H__

#include <stdint.h>
#include <cdefs.h>
#include <log.h>

/* Prototype for test case initializing & exit function */
typedef void (*test_init_t)(void);
typedef int (*test_run_t)(void);
typedef void (*test_exit_t)(void);

typedef struct test_case_desc {
    const char *name;
    test_init_t init;
    test_run_t run;
    test_exit_t exit;
} test_case_desc_t;

/* Convenience macros to declare a Test descriptor */
#define DECLARE_TEST_CASE(_name, _init, _run, _exit)  \
    static const test_case_desc_t __test_ ## _name  \
        __section(".test_case_descs") __used = {     \
        .name = #_name,                             \
        .init = (_init),                            \
        .run  = (_run),                             \
        .exit = (_exit)                             \
        }

IMPORT_SYM(uintptr_t, __test_case_descs_start__, test_case_descs_start);
IMPORT_SYM(uintptr_t, __test_case_descs_end__, test_case_descs_end);

static uint32_t __test_all_asserts = 0;
static uint32_t __test_failed_asserts = 0;

void __assert_func_failed(const char *file, int line, const char *func, const char *failedexpr);

void __assert_func_success(const char *file, int line, const char *func, const char *failedexpr);

#define assert(__e)                                                               \
    ((__e) ? __assert_func_success(__FILE__, __LINE__, __PRETTY_FUNCTION__, #__e) \
           : __assert_func_failed(__FILE__, __LINE__, __PRETTY_FUNCTION__, #__e))

#define RUN_CASE(_name, _func)                          \
    do {                                                \
        int __rc;                                       \
        __test_all_asserts = 0;                         \
        __test_failed_asserts = 0;                      \
        INFO("\n\033[32m[TEST] %s:\033[0m\n", _name); \
        __rc = _func();                                 \
    } while (0)

#define ASSERT_EQ(left, right)     \
    do {                           \
        assert((left) == (right)); \
    } while (0)

#define ASSERT_NEQ(left, right)    \
    do {                           \
        assert((left) != (right)); \
    } while (0)

#define ASSERT_TRUE(value) \
    do {                   \
        assert(value);     \
    } while (0)

#define ASSERT_FALSE(value) \
    do {                    \
        assert(!(value));   \
    } while (0)

#define ASSERT_EQ_FLOAT(left, right, sill)      \
    do {                                        \
        assert(abs((left) - (right)) < (sill)); \
    } while (0)

#endif /* __TESTS_LIB_H__ */
